
function reactive(obj) {
    const listeners = new Set();

    const proxy = new Proxy(obj, {
      get(target, property, receiver) {
        if (
          typeof target[property] === "object" &&
          target[property] !== null
        ) {
          return reactive(target[property]);
        }
        return Reflect.get(target, property, receiver);
      },
      set(target, property, value, receiver) {
        const result = Reflect.set(target, property, value, receiver);
        listeners.forEach((fn) => fn());
        return result;
      },
    });

    proxy.subscribe = function (fn) {
      listeners.add(fn);
    };

    proxy.unsubscribe = function (fn) {
      listeners.delete(fn);
    };

    return proxy;
  }

  class Component {
    constructor(options) {
      this.template = options.template;
      this.data = reactive(options.data());
      this.methods = options.methods;
      this.style = options.style;
      this.rootId = options.rootId;

      // Ensure root element exists
      if (!document.getElementById(this.rootId)) {
        const rootElement = document.createElement("div");
        rootElement.id = this.rootId;
        document.body.appendChild(rootElement);
      }

      this.data.subscribe(this.render.bind(this));
      this.render();
    }

    compileTemplate(template) {
      const match = template.match(/{{\s*(\w+)\s*}}/g);
      return () => {
        let compiledTemplate = template;
        if (match) {
          match.forEach((item) => {
            const key = item.replace(/{{\s*|\s*}}/g, "");
            compiledTemplate = compiledTemplate.replace(
              item,
              this.data[key],
            );
          });
        }
        return compiledTemplate;
      };
    }

    render() {
      const el = document.getElementById(this.rootId);
      if (el) {
        el.innerHTML = this.compileTemplate(this.template)();
        this.applyMethods(el);
      }
    }

    applyMethods(el) {
      Object.keys(this.methods).forEach((methodName) => {
        const matches = el.querySelectorAll(
          `[data-action="${methodName}"]`,
        );
        matches.forEach((match) => {
          match.onclick = this.methods[methodName].bind(this.data);
        });
      });
    }
  }
